#include "fxcc/core/graph/opengl3/IBuffers.h"

using namespace fxcc::graph::opengl3;
using namespace fxcc::graph::common;

fxcc::graph::opengl3::IBuffers::IBuffers()
{
    m_CubeMesh = std::make_shared<fxcc::graph::opengl3::MeshJack>(fxcc::graph::common::Geometry::CreateBox());
    m_SphereMesh = std::make_shared<fxcc::graph::opengl3::MeshJack>(fxcc::graph::common::Geometry::CreateSphere());
    m_QuadMesh = std::make_shared<fxcc::graph::opengl3::MeshJack>(fxcc::graph::common::Geometry::CreateQuad());
    m_Quad2Mesh = std::make_shared<fxcc::graph::opengl3::MeshJack>(fxcc::graph::common::Geometry::CreateQuad2());
    m_Texture2DMesh = std::make_shared<fxcc::graph::opengl3::MeshJack>(fxcc::graph::common::Geometry::Create2DTexture());

    m_PassBuffer = std::make_shared<fxcc::graph::opengl3::UniformBuffer>(sizeof(fxcc::graph::common::Effect3d::PassData));
    m_ObjBuffer = std::make_shared<fxcc::graph::opengl3::UniformBuffer>(sizeof(fxcc::graph::common::Effect3d::ObjData));
    m_BoneBuffer = std::make_shared<fxcc::graph::opengl3::UniformBuffer>(sizeof(fxcc::graph::common::Effect3d::BoneData));
    m_LightBuffer = std::make_shared<fxcc::graph::opengl3::UniformBuffer>(sizeof(fxcc::graph::common::LightEffect));
    
    Binding();

}

void fxcc::graph::opengl3::IBuffers::Binding()
{
    m_PassBuffer->Binding(0);
    m_ObjBuffer->Binding(1);
    m_BoneBuffer->Binding(2);
    m_LightBuffer->Binding(3);
}

void fxcc::graph::opengl3::IBuffers::SetObjData(const fxcc::graph::common::Transform& inst)
{
    fxcc::graph::common::Effect3d::ObjData objData;
    objData.Load(inst);
    m_ObjBuffer->Upload<Effect3d::ObjData>(objData);

}

void fxcc::graph::opengl3::IBuffers::SetObjData(const Effect3d::ObjData& inst)
{
    m_ObjBuffer->Upload<Effect3d::ObjData>(inst);
}

void fxcc::graph::opengl3::IBuffers::SetPassData(const Effect3d::PassData& inst)
{
    m_PassBuffer->Upload<Effect3d::PassData>(inst);
}

void fxcc::graph::opengl3::IBuffers::SetBoneData(const Effect3d::BoneData& inst)
{
    m_BoneBuffer->Upload<Effect3d::BoneData>(inst);
}

void fxcc::graph::opengl3::IBuffers::SetLightEffectData(const fxcc::graph::common::LightEffect& inst)
{
    m_LightBuffer->Upload<fxcc::graph::common::LightEffect>(inst);
}

fxcc::graph::opengl3::IBuffers::~IBuffers()
{
}

const std::string fxcc::graph::opengl3::IBuffers::GetUniformPart3d() 
{
    return  R"(

#define MAX_BONES_NUM 256
#define MAX_DIRECTION_NUM 8
#define MAX_POINT_NUM 8
#define MAX_SPOT_NUM 8

struct PassData
{
	mat4 proj;
	mat4 view;
	mat4 projView;
    mat4 skyboxView;
	vec3 viewPos;
	float nearZ;

	vec2 PassPad;
	int passId;
	float farZ;
};

struct ObjData
{
	mat4 world;
	mat4 worldInv;
	mat4 worldInvTranspose;
	int invertedNormal;
	float borderNormalAxis;
	int objId;
	float objBufferPad;

};

struct BoneData
{
	mat4 boneData[MAX_BONES_NUM];
};

struct DirLight {
    vec3 ambient;
    int ambientMap;

    vec3 diffuse;
    int diffuseMap;

    vec3 specular;
    int specularMap;

    vec3 direction;
    float pad;

};

struct   PointLight {
    vec3 ambient;
    int ambientMap;

    vec3 diffuse;
    int diffuseMap;

    vec3 specular;
    int specularMap;

    vec3 position;
    float pad3;

	vec3 lightPbrColor;
	float pad2;

    float constant;
    float linear;
    float quadratic;
    float pad4;
};

struct   SpotLight {
    vec3 ambient;
    int ambientMap;

    vec3 diffuse;
    int diffuseMap;

    vec3 specular;
    int specularMap;

    vec3 position;
    float cutOff;
    vec3 direction;
    float outerCutOff;
  
    float constant;
    float linear;
    float quadratic;
    float pad;
   
};


struct  LightEffect {

    DirLight dirLights[MAX_DIRECTION_NUM];
    PointLight pointLights[MAX_POINT_NUM];
    SpotLight spotLights[MAX_SPOT_NUM];

    int numDirs;
    int numPoints ;
    int numSpots;
    int pad;
};

uniform samplerCube irradianceMap;
uniform samplerCube prefilterMap;
uniform sampler2D brdfLUT;

struct VertexOut
{
	vec4 Position;
    vec3 NativePos;
	vec3 FragPos;
	vec2 TexCoords;
	vec3 Normal;
	vec4 ViewFragPos;
	vec3 ViewNormal;
};


layout(std140, binding=0) uniform PassBuffer
{
	PassData passData;
};

layout(std140, binding=1) uniform ObjBuffer
{
	ObjData objData;
};

layout(std140, binding=2) uniform BoneBuffer
{
	BoneData boneData;
};

layout(std140, binding=3) uniform LightBuffer
{
    LightEffect lightEffect3d;
};
)";
}

const std::string fxcc::graph::opengl3::IBuffers::GetVersionStr() 
{
    return R"(
#version 460 core

)";
}